import logging
import time
import random
from typing import Dict, Optional
from datetime import datetime, timedelta

from fastapi import APIRouter, HTTPException
import requests

from models.dida_auth import (
    DidaOAuthRequest,
    DidaRefreshTokenRequest,
    DidaBindingStatusResponse,
    DidaOAuthResponse,
)
from database import query, update, insert, get_db_type

logger = logging.getLogger(__name__)
router = APIRouter()

# 滴答清单OAuth配置
DIDA_OAUTH_BASE_URL = "https://dida365.com/oauth"
DIDA_API_BASE_URL = "https://api.dida365.com/open/v1"


class DidaOAuthClient:
    """滴答清单OAuth客户端"""

    def __init__(self, client_id: str, client_secret: str):
        self.client_id = client_id
        self.client_secret = client_secret

    def exchange_code_for_token(self, authorization_code: str, redirect_uri: str) -> Dict:
        """
        用授权码换取访问令牌

        Args:
            authorization_code: OAuth授权码
            redirect_uri: 重定向URI（必须与OAuth申请时一致）

        Returns:
            包含access_token、refresh_token等的字典
        """
        url = f"{DIDA_OAUTH_BASE_URL}/token"
        data = {
            "client_id": self.client_id,
            "client_secret": self.client_secret,
            "code": authorization_code,
            "grant_type": "authorization_code",
            "redirect_uri": redirect_uri,
        }

        try:
            logger.info(f"🔄 开始交换OAuth令牌")
            logger.info(f"   URL: {url}")
            logger.info(f"   client_id: {self.client_id}")
            logger.info(f"   client_secret: {self.client_secret[:10]}..." if self.client_secret else "   client_secret: None")
            logger.info(f"   code: {authorization_code}")
            logger.info(f"   redirect_uri: {redirect_uri}")
            
            response = requests.post(url, data=data, timeout=10)
            
            logger.info(f"📡 响应状态码: {response.status_code}")
            logger.info(f"📡 响应头: {dict(response.headers)}")
            
            # 记录详细的错误信息
            if response.status_code != 200:
                error_detail = response.text[:500]  # 只记录前500字符
                logger.error(f"❌ OAuth令牌交换失败 [{response.status_code}]")
                logger.error(f"   错误详情: {error_detail}")
                
                # 尝试解析JSON错误
                try:
                    error_json = response.json()
                    error_msg = error_json.get('error_description') or error_json.get('error') or error_detail
                except:
                    error_msg = error_detail
                
                raise HTTPException(
                    status_code=400, 
                    detail=f"OAuth令牌交换失败: {error_msg}"
                )
            
            result = response.json()
            logger.info(f"✅ OAuth令牌交换成功")

            if "access_token" not in result:
                raise ValueError(f"OAuth响应中缺少access_token: {result}")

            return result
        except HTTPException:
            raise
        except requests.exceptions.RequestException as e:
            logger.error(f"❌ OAuth令牌交换网络错误: {e}")
            raise HTTPException(status_code=400, detail=f"OAuth令牌交换失败: {str(e)}")

    def refresh_access_token(self, refresh_token: str) -> Dict:
        """
        刷新访问令牌

        Args:
            refresh_token: 刷新令牌

        Returns:
            包含新access_token的字典
        """
        url = f"{DIDA_OAUTH_BASE_URL}/token"
        data = {
            "client_id": self.client_id,
            "client_secret": self.client_secret,
            "refresh_token": refresh_token,
            "grant_type": "refresh_token",
        }

        try:
            response = requests.post(url, data=data, timeout=10)
            response.raise_for_status()
            result = response.json()

            if "access_token" not in result:
                raise ValueError(f"刷新令牌响应中缺少access_token: {result}")

            return result
        except requests.exceptions.RequestException as e:
            logger.error(f"刷新令牌失败: {e}")
            raise HTTPException(status_code=400, detail=f"刷新令牌失败: {str(e)}")

    def get_user_info(self, access_token: str) -> Dict:
        """
        获取用户信息
        注意：滴答清单Open API可能不提供用户详情接口，我们可以跳过这一步

        Args:
            access_token: 访问令牌

        Returns:
            用户信息字典（如果API不支持，返回空字典）
        """
        # 滴答清单Open API可能不提供标准的用户信息接口
        # 我们可以尝试获取项目列表来验证token有效性
        url = f"{DIDA_API_BASE_URL}/project"
        headers = {"Authorization": f"Bearer {access_token}"}

        try:
            response = requests.get(url, headers=headers, timeout=10)
            response.raise_for_status()
            # 如果能成功获取项目列表，说明token有效
            # 返回一个包含基本信息的字典
            projects = response.json()
            logger.info(f"Token验证成功，用户有 {len(projects) if isinstance(projects, list) else 0} 个项目")
            return {"verified": True, "project_count": len(projects) if isinstance(projects, list) else 0}
        except requests.exceptions.RequestException as e:
            logger.error(f"验证access_token失败: {e}")
            raise HTTPException(status_code=400, detail=f"验证access_token失败: {str(e)}")


@router.post("/oauth/callback", response_model=DidaOAuthResponse)
async def handle_oauth_callback(request: DidaOAuthRequest):
    """
    处理OAuth回调，交换授权码为访问令牌并保存

    Args:
        request: OAuth请求，包含授权码、Client凭证和redirect_uri

    Returns:
        OAuth响应
    """
    try:
        # 创建OAuth客户端
        oauth_client = DidaOAuthClient(request.client_id, request.client_secret)

        # 交换授权码为令牌（必须传递redirect_uri）
        logger.info(f"用户 {request.system_user_id} 正在交换OAuth授权码...")
        logger.info(f"redirect_uri: {request.redirect_uri}")
        token_result = oauth_client.exchange_code_for_token(
            authorization_code=request.authorization_code,
            redirect_uri=request.redirect_uri
        )

        logger.info(f"Token响应: {token_result}")
        
        access_token = token_result["access_token"]
        # refresh_token可能不存在，使用access_token作为备用
        refresh_token = token_result.get("refresh_token", access_token)
        expires_in = token_result.get("expires_in", 7200)  # 默认2小时
        
        if not token_result.get("refresh_token"):
            logger.warning("OAuth响应中未包含refresh_token，使用access_token作为备用")

        # 验证token并获取基本信息
        user_info = oauth_client.get_user_info(access_token)
        # 滴答清单可能不返回用户名，使用一个默认值
        dida_username = user_info.get("username") or user_info.get("email") or f"dida_user_{request.system_user_id}"
        logger.info(f"使用滴答清单账号名: {dida_username}")

        # 计算令牌过期时间
        token_expires_at = datetime.now() + timedelta(seconds=expires_in)

        # 保存到数据库
        await save_dida_credentials(
            system_user_id=request.system_user_id,
            dida_username=dida_username,
            client_id=request.client_id,
            client_secret=request.client_secret,
            access_token=access_token,
            refresh_token=refresh_token,
            token_expires_at=token_expires_at,
        )

        return DidaOAuthResponse(
            status="success",
            message="滴答清单账号绑定成功！",
            data={
                "username": dida_username,
                "expires_at": token_expires_at.isoformat(),
            },
        )

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"OAuth回调处理失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=f"服务器错误: {str(e)}")


@router.post("/refresh", response_model=DidaOAuthResponse)
async def refresh_dida_token(request: DidaRefreshTokenRequest):
    """
    刷新滴答清单访问令牌

    Args:
        request: 包含system_user_id的请求

    Returns:
        OAuth响应
    """
    try:
        # 查询当前凭证
        credentials = await get_dida_credentials(request.system_user_id)
        if not credentials:
            raise HTTPException(status_code=404, detail="未找到滴答清单绑定信息，请先绑定账号")

        # 创建OAuth客户端
        oauth_client = DidaOAuthClient(
            credentials["client_id"], credentials["client_secret"]
        )

        # 刷新令牌
        logger.info(f"用户 {request.system_user_id} 正在刷新滴答清单令牌...")
        token_result = oauth_client.refresh_access_token(credentials["refresh_token"])

        access_token = token_result["access_token"]
        refresh_token = token_result.get("refresh_token", credentials["refresh_token"])
        expires_in = token_result.get("expires_in", 7200)

        # 计算新的过期时间
        token_expires_at = datetime.now() + timedelta(seconds=expires_in)

        # 更新数据库
        await update_dida_credentials(
            system_user_id=request.system_user_id,
            access_token=access_token,
            refresh_token=refresh_token,
            token_expires_at=token_expires_at,
        )

        return DidaOAuthResponse(
            status="success",
            message="令牌刷新成功",
            data={
                "expires_at": token_expires_at.isoformat(),
            },
        )

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"刷新令牌失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=f"服务器错误: {str(e)}")


@router.get("/binding/status", response_model=DidaBindingStatusResponse)
async def check_binding_status(system_user_id: int):
    """
    检查滴答清单账号绑定状态

    Args:
        system_user_id: 系统用户ID

    Returns:
        绑定状态响应
    """
    try:
        logger.info(f"🔍 检查用户 {system_user_id} 的滴答清单绑定状态")

        credentials = await get_dida_credentials(system_user_id)

        if credentials:
            logger.info(f"✅ 找到绑定账号: {credentials['dida_username']}")
            return DidaBindingStatusResponse(
                is_bound=True,
                username=credentials["dida_username"],
                bound_at=str(credentials["created_at"]),
                token_expires_at=str(credentials.get("token_expires_at")),
            )

        logger.info(f"❌ 未找到绑定账号，system_user_id={system_user_id}")
        return DidaBindingStatusResponse(is_bound=False)

    except Exception as e:
        logger.error(f"❌ check_binding_status error: {e}", exc_info=True)
        return DidaBindingStatusResponse(is_bound=False)


@router.delete("/unbind")
async def unbind_dida_account(system_user_id: int):
    """
    解绑滴答清单账号

    Args:
        system_user_id: 系统用户ID

    Returns:
        操作结果
    """
    try:
        db_type = get_db_type()

        if db_type == "mysql":
            # MySQL: 软删除（设置is_active=0）
            update_sql = """
                UPDATE dida_credentials 
                SET is_active = 0 
                WHERE system_user_id = %s
            """
            update(update_sql, (system_user_id,))
        else:
            # StarRocks: 由于是DUPLICATE KEY表，不支持DELETE，插入一条标记删除的记录
            # 这里简单起见，我们不真正删除，只是在查询时忽略
            logger.warning("StarRocks不支持DELETE，请手动处理或在查询时过滤")

        return {"status": "success", "message": "滴答清单账号已解绑"}

    except Exception as e:
        logger.error(f"解绑账号失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=f"解绑失败: {str(e)}")


# ==================== 数据库操作辅助函数 ====================


async def save_dida_credentials(
    system_user_id: int,
    dida_username: str,
    client_id: str,
    client_secret: str,
    access_token: str,
    refresh_token: str,
    token_expires_at: datetime,
):
    """
    保存滴答清单凭证到数据库

    Args:
        system_user_id: 系统用户ID
        dida_username: 滴答清单用户名
        client_id: 应用Client ID
        client_secret: 应用Client Secret
        access_token: 访问令牌
        refresh_token: 刷新令牌
        token_expires_at: 令牌过期时间
    """
    try:
        db_type = get_db_type()

        if db_type == "mysql":
            # MySQL: 先禁用旧凭证，再插入新凭证
            update_sql = """
                UPDATE dida_credentials 
                SET is_active = 0 
                WHERE system_user_id = %s
            """
            update(update_sql, (system_user_id,))

            insert_sql = """
                INSERT INTO dida_credentials 
                (system_user_id, dida_username, client_id, client_secret, 
                 access_token, refresh_token, token_expires_at, is_active, created_at, updated_at)
                VALUES (%s, %s, %s, %s, %s, %s, %s, 1, NOW(), NOW())
            """
            insert(
                insert_sql,
                (
                    system_user_id,
                    dida_username,
                    client_id,
                    client_secret,
                    access_token,
                    refresh_token,
                    token_expires_at,
                ),
            )
            logger.info(f"[MySQL] 用户 {system_user_id} 成功绑定滴答清单账号: {dida_username}")

        else:  # StarRocks
            # StarRocks: 直接插入新记录
            credential_id = int(time.time() * 1000) + random.randint(1000, 9999)
            insert_sql = """
                INSERT INTO dida_credentials 
                (id, system_user_id, dida_username, client_id, client_secret, 
                 access_token, refresh_token, token_expires_at, created_at, updated_at)
                VALUES (%s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())
            """
            insert(
                insert_sql,
                (
                    credential_id,
                    system_user_id,
                    dida_username,
                    client_id,
                    client_secret,
                    access_token,
                    refresh_token,
                    token_expires_at,
                ),
            )
            logger.info(f"[StarRocks] 用户 {system_user_id} 成功绑定滴答清单账号: {dida_username}")

    except Exception as e:
        logger.error(f"save_dida_credentials error: {e}")
        raise


async def update_dida_credentials(
    system_user_id: int,
    access_token: str,
    refresh_token: str,
    token_expires_at: datetime,
):
    """
    更新滴答清单凭证（主要用于刷新令牌）

    Args:
        system_user_id: 系统用户ID
        access_token: 新的访问令牌
        refresh_token: 新的刷新令牌
        token_expires_at: 新的过期时间
    """
    try:
        db_type = get_db_type()

        if db_type == "mysql":
            update_sql = """
                UPDATE dida_credentials 
                SET access_token = %s, 
                    refresh_token = %s,
                    token_expires_at = %s,
                    updated_at = NOW()
                WHERE system_user_id = %s AND is_active = 1
            """
            update(update_sql, (access_token, refresh_token, token_expires_at, system_user_id))
            logger.info(f"[MySQL] 用户 {system_user_id} 的滴答清单令牌已更新")
        else:
            # StarRocks: 插入新记录
            credentials = await get_dida_credentials(system_user_id)
            if credentials:
                await save_dida_credentials(
                    system_user_id=system_user_id,
                    dida_username=credentials["dida_username"],
                    client_id=credentials["client_id"],
                    client_secret=credentials["client_secret"],
                    access_token=access_token,
                    refresh_token=refresh_token,
                    token_expires_at=token_expires_at,
                )

    except Exception as e:
        logger.error(f"update_dida_credentials error: {e}")
        raise


async def get_dida_credentials(system_user_id: int) -> Optional[Dict]:
    """
    获取用户的滴答清单凭证

    Args:
        system_user_id: 系统用户ID

    Returns:
        凭证字典，如果不存在则返回None
    """
    try:
        db_type = get_db_type()

        if db_type == "mysql":
            sql = """
                SELECT dida_username, client_id, client_secret, access_token, 
                       refresh_token, token_expires_at, created_at, updated_at
                FROM dida_credentials 
                WHERE system_user_id = %s AND is_active = 1
                ORDER BY updated_at DESC 
                LIMIT 1
            """
        else:
            # StarRocks: 取最新记录
            sql = """
                SELECT dida_username, client_id, client_secret, access_token, 
                       refresh_token, token_expires_at, created_at, updated_at
                FROM dida_credentials 
                WHERE system_user_id = %s
                ORDER BY updated_at DESC 
                LIMIT 1
            """

        result = query(sql, (system_user_id,))

        if result and len(result) > 0:
            return result[0]

        return None

    except Exception as e:
        logger.error(f"get_dida_credentials error: {e}")
        return None

